package com.kexio.security.domain;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.kexio.core.data.domain.BaseEntity;
import com.kexio.security.domain.enums.DeviceType;
import com.kexio.security.domain.enums.SessionStatus;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

/**
 * 登录会话实体
 * 
 * @author Kexio Team
 * @since 1.0.0
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_login_session")
@Schema(description = "登录会话实体")
public class LoginSession extends BaseEntity {

    /**
     * 用户ID
     */
    @NotNull(message = "用户ID不能为空")
    @TableField("user_id")
    @Schema(description = "用户ID", example = "1")
    private Long userId;

    /**
     * 用户名
     */
    @NotBlank(message = "用户名不能为空")
    @Size(max = 50, message = "用户名长度不能超过50")
    @TableField("username")
    @Schema(description = "用户名", example = "admin")
    private String username;

    /**
     * 会话ID
     */
    @NotBlank(message = "会话ID不能为空")
    @Size(max = 128, message = "会话ID长度不能超过128")
    @TableField("session_id")
    @Schema(description = "会话ID", example = "sess_1234567890")
    private String sessionId;

    /**
     * 访问令牌
     */
    @JsonIgnore
    @TableField("access_token")
    @Schema(description = "访问令牌", hidden = true)
    private String accessToken;

    /**
     * 刷新令牌
     */
    @JsonIgnore
    @TableField("refresh_token")
    @Schema(description = "刷新令牌", hidden = true)
    private String refreshToken;

    /**
     * 设备类型
     */
    @TableField("device_type")
    @Schema(description = "设备类型", example = "web")
    private String deviceType;

    /**
     * 设备信息
     */
    @Size(max = 500, message = "设备信息长度不能超过500")
    @TableField("device_info")
    @Schema(description = "设备信息", example = "Chrome/120.0.0.0")
    private String deviceInfo;

    /**
     * 登录IP地址
     */
    @Size(max = 50, message = "IP地址长度不能超过50")
    @TableField("login_ip")
    @Schema(description = "登录IP地址", example = "192.168.1.100")
    private String loginIp;

    /**
     * 登录位置
     */
    @Size(max = 100, message = "登录位置长度不能超过100")
    @TableField("login_location")
    @Schema(description = "登录位置", example = "北京市")
    private String loginLocation;

    /**
     * 登录时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField("login_time")
    @Schema(description = "登录时间", example = "2024-01-01 12:00:00")
    private LocalDateTime loginTime;

    /**
     * 最后访问时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField("last_access_time")
    @Schema(description = "最后访问时间", example = "2024-01-01 12:30:00")
    private LocalDateTime lastAccessTime;

    /**
     * 过期时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField("expire_time")
    @Schema(description = "过期时间", example = "2024-01-01 20:00:00")
    private LocalDateTime expireTime;

    /**
     * 会话状态
     */
    @TableField("status")
    @Schema(description = "会话状态", example = "1")
    private Integer status;

    /**
     * 租户ID
     */
    @TableField("tenant_id")
    @Schema(description = "租户ID", example = "1")
    private Long tenantId;

    /**
     * 备注
     */
    @Size(max = 500, message = "备注长度不能超过500")
    @TableField("remark")
    @Schema(description = "备注", example = "正常登录")
    private String remark;

    // =========================== 便利方法 ===========================

    /**
     * 获取设备类型枚举
     * @return 设备类型枚举
     */
    public DeviceType getDeviceType() {
        return DeviceType.fromCode(this.deviceType);
    }

    /**
     * 设置设备类型
     * @param deviceType 设备类型枚举
     */
    public void setDeviceType(DeviceType deviceType) {
        this.deviceType = deviceType != null ? deviceType.getCode() : null;
    }

    /**
     * 获取会话状态枚举
     * @return 会话状态枚举
     */
    public SessionStatus getSessionStatus() {
        return SessionStatus.fromCode(this.status);
    }

    /**
     * 设置会话状态
     * @param sessionStatus 会话状态枚举
     */
    public void setSessionStatus(SessionStatus sessionStatus) {
        this.status = sessionStatus != null ? sessionStatus.getCode() : null;
    }

    /**
     * 检查会话是否活跃
     * @return 是否活跃
     */
    public boolean isActive() {
        SessionStatus sessionStatus = getSessionStatus();
        return sessionStatus != null && sessionStatus.isActive() && !isExpired();
    }

    /**
     * 检查会话是否已过期
     * @return 是否已过期
     */
    public boolean isExpired() {
        if (expireTime == null) {
            return false;
        }
        return LocalDateTime.now().isAfter(expireTime);
    }

    /**
     * 检查会话是否可以刷新
     * @return 是否可刷新
     */
    public boolean canRefresh() {
        SessionStatus sessionStatus = getSessionStatus();
        return sessionStatus != null && sessionStatus.canRefresh() && !isExpired();
    }

    /**
     * 更新最后访问时间
     */
    public void updateLastAccessTime() {
        this.lastAccessTime = LocalDateTime.now();
    }

    /**
     * 延长会话过期时间
     * @param minutes 延长分钟数
     */
    public void extendExpiration(long minutes) {
        if (expireTime != null) {
            this.expireTime = expireTime.plus(minutes, ChronoUnit.MINUTES);
        }
    }

    /**
     * 设置会话过期时间
     * @param minutes 过期分钟数（从当前时间开始）
     */
    public void setExpirationFromNow(long minutes) {
        this.expireTime = LocalDateTime.now().plus(minutes, ChronoUnit.MINUTES);
    }

    /**
     * 获取会话持续时间（分钟）
     * @return 会话持续时间
     */
    public long getDurationMinutes() {
        if (loginTime == null) {
            return 0;
        }
        LocalDateTime endTime = lastAccessTime != null ? lastAccessTime : LocalDateTime.now();
        return ChronoUnit.MINUTES.between(loginTime, endTime);
    }

    /**
     * 获取剩余有效时间（分钟）
     * @return 剩余有效时间，如果已过期返回0
     */
    public long getRemainingMinutes() {
        if (expireTime == null) {
            return Long.MAX_VALUE; // 永不过期
        }
        
        LocalDateTime now = LocalDateTime.now();
        if (now.isAfter(expireTime)) {
            return 0; // 已过期
        }
        
        return ChronoUnit.MINUTES.between(now, expireTime);
    }

    /**
     * 终止会话
     * @param reason 终止原因
     */
    public void terminate(SessionStatus reason) {
        setSessionStatus(reason);
        this.expireTime = LocalDateTime.now();
    }

    /**
     * 激活会话
     * @param expirationMinutes 过期分钟数
     */
    public void activate(long expirationMinutes) {
        setSessionStatus(SessionStatus.ACTIVE);
        this.loginTime = LocalDateTime.now();
        this.lastAccessTime = LocalDateTime.now();
        setExpirationFromNow(expirationMinutes);
    }
}
